package dao

import (
	"context"
	"gorm.io/gorm/clause"
	"mall/api/inventory"
	"mall/api/mall"
	"mall/api/pay"
	"mall/internal/model"
	"mall/pkg/util"
	"time"
)

// Lock 锁定请求
func (d *Dao) LockRequest(ctx context.Context, request *model.OrderRequest, requestId string) error {
	return d.processSqlError(d.TransContext(ctx).Model(&model.OrderRequest{}).Clauses(
		clause.Locking{Strength: "UPDATE"}).Where(
		"biz_no = ?", requestId).Take(request).Error)
}

// Lock 锁定请求
func (d *Dao) LockRequestById(ctx context.Context, request *model.OrderRequest, requestId uint64) error {
	return d.processSqlError(d.TransContext(ctx).Model(&model.OrderRequest{}).Clauses(
		clause.Locking{Strength: "UPDATE"}).Where(
		"id = ?", requestId).Take(request).Error)
}

// Lock 锁定商城订单
func (d *Dao) LockOrder(ctx context.Context, order *model.Order, orderNumber string) error {
	return d.processSqlError(d.TransContext(ctx).Model(&model.Order{}).Clauses(
		clause.Locking{Strength: "UPDATE"}).Where(
		"order_number = ?", orderNumber).Take(order).Error)
}

// Lock 锁定商城订单
func (d *Dao) LockOrderByID(ctx context.Context, order *model.Order, orderId uint64) error {
	return d.processSqlError(d.TransContext(ctx).Model(&model.Order{}).Clauses(
		clause.Locking{Strength: "UPDATE"}).Where(
		"id = ?", orderId).Take(order).Error)
}

func (d *Dao) GetSchedule(ctx context.Context, scheduleType uint8, orderId uint64) (
	schedule *model.Schedule, err error) {
	schedule = &model.Schedule{}
	if err = d.TransContext(ctx).Model(&model.Schedule{}).Where(
		"schedule_type = ? AND order_id = ?",
		scheduleType, orderId).Take(schedule).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) DeleteSchedule(ctx context.Context, scheduleType uint8, orderId uint64) (err error) {
	if err = d.TransContext(ctx).Delete(&model.Schedule{},
		"schedule_type = ? and order_id = ?",
		scheduleType, orderId).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	return
}

func (d *Dao) UpdateOrderStatus(ctx context.Context, requestId uint64, status uint8) (err error) {
	if err = d.TransContext(ctx).Model(&model.Order{}).Where(
		"request_id = ?",
		requestId).Update("status", status).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	return
}

func (d *Dao) FindMemberOrdersByPage(ctx context.Context, sgId, pageNo, pageSize uint32,
	memberIds []int64, category mall.MallCustomerCategory, query string) (
	orders []*model.Order, err error) {
	db := d.TransContext(ctx).Model(&model.Order{}).Where(
		"member_id in ? AND sg_id = ?", memberIds, sgId)
	switch category {
	case mall.MallCustomerCategory_MALL_CUSTOMER_CATEGORY_WAITING_PAY:
		db = db.Where("status = ?", int(mall.MallOrderStatus_MALL_INIT))
	case mall.MallCustomerCategory_MALL_CUSTOMER_CATEGORY_WAITING_SEND:
		db = db.Where("inventory_status in ?", []int{
			int(inventory.ProcessOrderStatus_ProcessOrderWaitStock),
			int(inventory.ProcessOrderStatus_ProcessOrderWaitShip)})
	case mall.MallCustomerCategory_MALL_CUSTOMER_CATEGORY_WAITING_SIGN:
		db = db.Where("inventory_status in ?", []int{
			int(inventory.ProcessOrderStatus_ProcessOrderWaitMentioned),
			int(inventory.ProcessOrderStatus_ProcessOrderShipped)})
	}
	if query != "" {
		db = db.Joins("JOIN mall_order_detail ON order_id = mall_order.id").Where(
			"prod_name like ?", "%"+query+"%")
	}
	err = d.processSqlError(db.Offset(int((pageNo - 1) * pageSize)).Limit(int(pageSize)).Order(
		"id desc").Scan(&orders).Error)
	return
}

func (d *Dao) FindOrderRequestByIds(ctx context.Context, requestIds []uint64) (
	requests []*model.OrderRequest, err error) {
	if err = d.TransContext(ctx).Model(&model.OrderRequest{}).Where(
		"id in ?",
		requestIds).Scan(&requests).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindOrdersByRequestId(ctx context.Context, requestId uint64) (
	orders []*model.Order, err error) {
	err = d.processSqlError(d.TransContext(ctx).Model(&model.Order{}).Where(
		"request_id = ?", requestId).Scan(&orders).Error)
	return
}

func (d *Dao) FindOrdersByOrderIds(ctx context.Context, orderIds []uint64) (
	orders []*model.Order, err error) {
	err = d.processSqlError(d.TransContext(ctx).Model(&model.Order{}).Where(
		"id in ?", orderIds).Scan(&orders).Error)
	return
}

func (d *Dao) FindOrderDetailByOrderIds(ctx context.Context, orderIds []uint64) (
	details []*model.OrderDetail, err error) {
	if err = d.TransContext(ctx).Model(&model.OrderDetail{}).Where(
		"order_id in ?",
		orderIds).Scan(&details).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindRefundDetailsByApplyIds(ctx context.Context, applyIds []uint64) (
	details []*model.RefundDetail, err error) {
	if err = d.TransContext(ctx).Model(&model.RefundDetail{}).Where(
		"apply_id in ?",
		applyIds).Scan(&details).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindRefundDetailByOrderId(ctx context.Context, orderId uint64) (
	details []*model.RefundDetailModel, err error) {
	if err = d.TransContext(ctx).Model(&model.RefundDetail{}).Joins(
		"JOIN mall_refund ON apply_id = mall_refund.id").Select(
		"detail_id, apply_number, status, pay_status, inventory_status, mall_refund_detail.trans, "+
			"COALESCE(warehouse_info::JSON -> 'platform') as platform, "+
			"COALESCE(warehouse_info::JSON -> 'platform_order_num') as platform_order_num,"+
			"apply_type ").Where(
		"mall_refund_detail.order_id = ?",
		orderId).Scan(&details).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindOrderDiscountsByOrderId(ctx context.Context, orderId uint64) (
	discounts []*model.OrderDiscount, err error) {
	if err = d.TransContext(ctx).Model(&model.OrderDiscount{}).Where(
		"order_id = ?",
		orderId).Scan(&discounts).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) GetOrderByOrderNumber(ctx context.Context, orderNumber string) (
	order *model.Order, err error) {
	order = &model.Order{}
	if err = d.TransContext(ctx).Model(&model.Order{}).Where(
		"order_number = ?",
		orderNumber).Take(order).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) GetOrderByOrderID(ctx context.Context, orderId uint64) (
	order *model.Order, err error) {
	order = &model.Order{}
	if err = d.TransContext(ctx).Model(&model.Order{}).Where(
		"id = ?",
		orderId).Take(order).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) GetOrderRequestByID(ctx context.Context, requestId uint64) (
	request *model.OrderRequest, err error) {
	request = &model.OrderRequest{}
	if err = d.TransContext(ctx).Model(&model.OrderRequest{}).Where(
		"id = ?",
		requestId).Take(request).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) GetOrderRequestByBizNo(ctx context.Context, bizNo string) (
	request *model.OrderRequest, err error) {
	request = &model.OrderRequest{}
	if err = d.TransContext(ctx).Model(&model.OrderRequest{}).Where(
		"biz_no = ?", bizNo).Take(request).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindOrderDigestsByRequestID(ctx context.Context, requestId uint64) (
	orders []*model.OrderDigestModel, err error) {
	if err = d.TransContext(ctx).Model(&model.Order{}).Joins(
		"JOIN mall_order_detail ON order_id = mall_order.id").Select(
		"deliver_type, mall_order.trans, SUM(quantity) as prod_count,"+
			"COALESCE(inventory_remark::JSON -> 'warehouse_name') as warehouse_name").Where(
		"request_id = ?",
		requestId).Group("mall_order.id").Scan(&orders).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) CloseMallOrdersByRequestID(ctx context.Context, requestId uint64, status uint8) (err error) {
	err = d.processSqlError(d.TransContext(ctx).Model(&model.Order{}).Where(
		"request_id = ?", requestId).Updates(map[string]interface{}{
		"status":        status,
		"complete_time": time.Now(),
	}).Error)
	return
}

func (d *Dao) UpdateDetailIsRefundingByOrderId(ctx context.Context, orderId uint64, isRefunding bool) (
	err error) {
	err = d.processSqlError(d.TransContext(ctx).Model(&model.OrderDetail{}).Where(
		"order_id = ?", orderId).Updates(map[string]interface{}{
		"is_refunding": isRefunding,
	}).Error)
	return
}

func (d *Dao) UpdateDetailIsRefundingByDetailIds(ctx context.Context, detailIds []uint64, isRefunding bool) (
	err error) {
	err = d.processSqlError(d.TransContext(ctx).Model(&model.OrderDetail{}).Where(
		"id in ?", detailIds).Updates(map[string]interface{}{
		"is_refunding": isRefunding,
	}).Error)
	return
}

func (d *Dao) FindMemberRefundsByPage(ctx context.Context, sgId, pageNo, pageSize uint32,
	memberIds []int64) (refunds []*model.Refund, err error) {
	db := d.TransContext(ctx).Model(&model.Refund{}).Where(
		"member_id in ? AND sg_id = ?", memberIds, sgId)
	err = d.processSqlError(db.Offset(int((pageNo - 1) * pageSize)).Limit(int(pageSize)).Order(
		"id desc").Scan(&refunds).Error)
	return
}

func (d *Dao) FindOrderRefundsByOrderIds(ctx context.Context, orderIds []uint64) (
	refunds []*model.Refund, err error) {
	err = d.processSqlError(d.TransContext(ctx).Model(&model.Refund{}).Where(
		"order_id in ?", orderIds).Scan(&refunds).Error)
	return
}

func (d *Dao) GetApplyByApplyNumber(ctx context.Context, applyNumber string) (
	apply *model.Refund, err error) {
	apply = &model.Refund{}
	if err = d.TransContext(ctx).Model(&model.Refund{}).Where(
		"apply_number = ?",
		applyNumber).Take(apply).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

// Lock 锁定售后单
func (d *Dao) LockApply(ctx context.Context, apply *model.Refund, applyNumber string) error {
	return d.processSqlError(d.TransContext(ctx).Model(&model.Refund{}).Clauses(
		clause.Locking{Strength: "UPDATE"}).Where(
		"apply_number = ?", applyNumber).Take(apply).Error)
}

// Lock 锁定售后单
func (d *Dao) LockApplyByID(ctx context.Context, apply *model.Refund, applyID uint64) error {
	return d.processSqlError(d.TransContext(ctx).Model(&model.Refund{}).Clauses(
		clause.Locking{Strength: "UPDATE"}).Where(
		"id = ?", applyID).Take(apply).Error)
}

func getEndDate(endDate string) string {
	endDateTime, _ := util.ParseTime(endDate)
	return endDateTime.AddDate(0, 0, 1).Format(util.DateLayout)
}

func (d *Dao) FindWebOrdersByPage(ctx context.Context, sgId, pStoreId, pageNo, pageSize uint32,
	startDate, endDate string, status mall.MallWebOrderStatus,
	memberIds []int64, queryOrderNumber string, warehouseId uint32) (
	orders []*model.Order, total int64, err error) {
	db := d.TransContext(ctx).Model(&model.Order{}).Where(
		"sg_id = ? AND p_store_id = ? "+
			"AND submit_time BETWEEN ? AND ?",
		sgId, pStoreId, startDate, getEndDate(endDate))
	switch status {
	case mall.MallWebOrderStatus_MALL_WEB_ORDER_WAITING_PAY:
		db = db.Where("status = ?", int(mall.MallOrderStatus_MALL_INIT))
	case mall.MallWebOrderStatus_MALL_WEB_ORDER_WAITING_SEND:
		db = db.Where("inventory_status in ? AND status = ?", []int{
			int(inventory.ProcessOrderStatus_ProcessOrderWaitStock),
			int(inventory.ProcessOrderStatus_ProcessOrderWaitShip),
		}, uint8(mall.MallOrderStatus_MALL_PAYED))
	case mall.MallWebOrderStatus_MALL_WEB_ORDER_WAITING_SIGN:
		db = db.Where("inventory_status in ? AND status = ?", []int{
			int(inventory.ProcessOrderStatus_ProcessOrderWaitMentioned),
			int(inventory.ProcessOrderStatus_ProcessOrderShipped),
		}, uint8(mall.MallOrderStatus_MALL_PAYED))
	case mall.MallWebOrderStatus_MALL_WEB_ORDER_FINISHED:
		db = db.Where("inventory_status = ? AND status = ?",
			int(inventory.ProcessOrderStatus_ProcessOrderReceived),
			uint8(mall.MallOrderStatus_MALL_PAYED))
	case mall.MallWebOrderStatus_MALL_WEB_ORDER_CLOSED:
		db = db.Where("status in ?", []int{
			int(mall.MallOrderStatus_MALL_CLOSED_BEFORE_PAYED),
			int(mall.MallOrderStatus_MALL_CLOSED_AFTER_PAYED),
		})
	}
	if len(memberIds) != 0 {
		db = db.Where("member_id in ?", memberIds)
	}
	if warehouseId != 0 {
		db = db.Where("warehouse_id = ?", warehouseId)
	}
	if queryOrderNumber != "" {
		db = db.Where("order_number = ?", queryOrderNumber)
	}
	if err = db.Count(&total).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	err = d.processSqlError(db.Offset(int((pageNo - 1) * pageSize)).Limit(int(pageSize)).Order(
		"id desc").Scan(&orders).Error)
	return
}

func (d *Dao) FindWebRefundsByPage(ctx context.Context, sgId, pStoreId, pageNo, pageSize uint32,
	startDate, endDate string, category mall.MallWebRefundCategory, refundType mall.MallRefundType,
	orderId uint64, applyNo string) (refunds []*model.Refund, total int64, err error) {
	db := d.TransContext(ctx).Model(&model.Refund{}).Where(
		"sg_id = ? AND p_store_id = ? AND apply_time BETWEEN ? AND ?",
		sgId, pStoreId, startDate, getEndDate(endDate))
	switch category {
	case mall.MallWebRefundCategory_MALL_WEB_REFUND_CATEGORY_PROCESSING:
		db = db.Where("(status = ? OR pay_status = ? OR inventory_status = ?)",
			int(mall.MallRefundStatus_MALL_REFUND_INIT),
			int(pay.PayOrderStatus_REFUND_ORDER_STATUS_FAILED),
			int(inventory.ProcessOrderStatus_ProcessOrderReturning))
	case mall.MallWebRefundCategory_MALL_WEB_REFUND_CATEGORY_SUCCESS:
		db = db.Where("pay_status in ?", []int{
			int(pay.PayOrderStatus_REFUND_ORDER_STATUS_SUCCESS),
			int(pay.PayOrderStatus_REFUND_ORDER_STATUS_MARKED_SUCCESS)})
	case mall.MallWebRefundCategory_MALL_WEB_REFUND_CATEGORY_REJECTED:
		db = db.Where("status = ? OR inventory_status = ?",
			int(mall.MallRefundStatus_MALL_REFUND_REJECTED),
			int(inventory.ProcessOrderStatus_ProcessOrderRejectReturn))
	case mall.MallWebRefundCategory_MALL_WEB_REFUND_CATEGORY_CLOSED:
		db = db.Where("status = ?", uint8(mall.MallRefundStatus_MALL_REFUND_CLOSED))
	}
	if refundType != mall.MallRefundType_MALL_REFUND_TYPE_ALL {
		db = db.Where("apply_type = ?", uint8(refundType))
	}
	if orderId != 0 {
		db = db.Where("order_id = ?", orderId)
	}
	if applyNo != "" {
		db = db.Where("apply_number = ?", applyNo)
	}
	if err = db.Count(&total).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	err = d.processSqlError(db.Offset(int((pageNo - 1) * pageSize)).Limit(int(pageSize)).Order(
		"mall_refund.id desc").Scan(&refunds).Error)
	return
}
